import 'panorama-polyfill-x/lib/console';
import 'panorama-polyfill-x/lib/timers';

interface RootPanel extends Panel {
    loaded: boolean; // 记录是否加载过初始化
    GridNav: any[]; //记录网格点
    squareX: number;
    squareY: number;
    boundX: number;
    boundY: number;
}

var state = 'disabled';
var frame_rate = 1 / 30;
var tree_update_interval = 1;
var size = 0;
var overlay_size = 0;
var range = 0;
var pressedShift = false;
var altDown = false;
var requires: number;
var modelParticle: ParticleID;
var propParticle: ParticleID;
var propScale;
var offsetZ: any;
var modelOffset: any;
var gridParticles: any[];
var overlayParticles: any[];
var rangeOverlay: ParticleID;
var rangeOverlayActive: boolean;
var builderIndex: EntityIndex;
var entityGrid: any[] = [];
var tree_entities: any[] = [];
var distance_to_gold_mine: number;
var last_tree_update = Game.GetGameTime();
var treeGrid: any[] = [];
var cutTrees: any = {};
var localHeroIndex;
var Root = $.GetContextPanel() as RootPanel;

/** settings */
var grid_alpha = 30;
var alt_grid_alpha = 90;
var alt_grid_squares = 4;
var range_overlay_alpha = 30;
var model_alpha = 100;
var recolor_ghost = true;
var turn_red = true;
var permanent_alt_grid = false;
var update_trees = false;
var height_restriction: number;

var GRID_TYPES = {
    BUILDABLE: '',
    BLOCKED: '',
    '': '',
};

/**
 * 初始化
 */
const _init_Sz_Building_helper = () => {
    if (!Root.loaded) {
        Root.GridNav = [];
        Root.squareX = 0;
        Root.squareY = 0;
        Root.loaded = true;
    }

    RequestGNV();

    GameEvents.Subscribe('building_helper_enable', StartBuildingHelper);
    GameEvents.Subscribe('building_helper_end', EndBuildingHelper);
    GameEvents.Subscribe('gnv_register', RegisterGNV);
};
/**
 * 注册网格
 * @param msg
 */
const RegisterGNV = (msg: any) => {
    var GridNav: any[] = [];
    var squareX = msg.squareX;
    var squareY = msg.squareY;
    var boundX = msg.boundX;
    var boundY = msg.boundY;
    $.Msg('Registering GNV [' + squareX + ',' + squareY + '] ', 'Min Bounds: X=' + boundX + ', Y=' + boundY);
    $.Msg('gnvRLELen: ', msg.gnv.length);
    var arr = [];
    // Running-length decoding
    var strlength = '';
    for (var i = 0; i < msg.gnv.length; i++) {
        var chr = msg.gnv.charCodeAt(i);
        if (chr >= 48 && chr <= 57) {
            strlength = strlength + String.fromCharCode(chr);
        } else {
            var num = parseInt(strlength);
            for (var j = 0; j < num; j++) {
                arr.push(String.fromCharCode(chr));
            }
            strlength = '';
        }
    }
    msg.gnv = arr.join('');
    arr = [];
    // Thanks to BMD for this method
    for (var i = 0; i < msg.gnv.length; i++) {
        var code = msg.gnv.charCodeAt(i) - 58;
        for (var j = 4; j >= 0; j -= 2) {
            var g = (code & (3 << j)) >> j;
            if (g != 0) arr.push(g);
        }
    }

    // Load the GridNav
    var x = 0;
    for (var i = 0; i < squareX; i++) {
        GridNav[i] = [];
        for (var j = 0; j < squareY; j++) {
            GridNav[i][j] = arr[x] == 1 ? GRID_TYPES['BUILDABLE'] : GRID_TYPES['BLOCKED'];
            x++;
        }
    }
    Root.GridNav = GridNav;
    Root.squareX = squareX;
    Root.squareY = squareY;
    Root.boundX = boundX;
    Root.boundY = boundY;

    var tab = { 0: 0, 1: 0, 2: 0, 3: 0 } as any;
    for (i = 0; i < arr.length; i++) {
        tab[arr[i].toString()]++;
    }
    $.Msg('Free: ', tab['1'], ' Blocked: ', tab['2'], ' gnvLen: ', msg.gnv.length);
};
/**
 * 请求网格
 */
const RequestGNV = () => {
    GameEvents.SendCustomGameEventToServer('gnv_request', {} as never);
};
function Length2D(v1: [number, number, number], v2: [number, number, number]) {
    return Math.sqrt((v2[0] - v1[0]) * (v2[0] - v1[0]) + (v2[1] - v1[1]) * (v2[1] - v1[1]));
}
function GetClosestDistanceToGoldMine(position: [number, number, number]) {
    var building_entities = Entities.GetAllEntitiesByClassname('npc_dota_building');

    var minDistance = 99999;
    for (var i = 0; i < building_entities.length; i++) {
        if (Entities.GetUnitName(building_entities[i]) == 'gold_mine') {
            var distance_to_this_mine = Length2D(position, Entities.GetAbsOrigin(building_entities[i]));
            if (distance_to_this_mine < minDistance) minDistance = distance_to_this_mine;
        }
    }
    return minDistance;
}
function TooCloseToGoldmine(position: [number, number, number]) {
    return distance_to_gold_mine > 0 && GetClosestDistanceToGoldMine(position) < distance_to_gold_mine;
}
function WorldToGridPosX(x: number) {
    return Math.floor(x / 64);
}

function WorldToGridPosY(y: number) {
    return Math.floor(y / 64);
}

function BlockEntityGrid(position: [number, number, number], gridType: string) {
    var y = WorldToGridPosX(position[0]) - Root.boundX;
    var x = WorldToGridPosY(position[1]) - Root.boundY;

    if (entityGrid[x] === undefined) entityGrid[x] = [];
    if (entityGrid[x][y] === undefined) entityGrid[x][y] = 0;

    // entityGrid[x][y] = entityGrid[x][y] | gridType;

    entityGrid[x][y] = entityGrid[x][y];
}

/**
 * 获取读取的格子类型
 * @param entIndex
 * @returns
 */
function GetRequiredGridType(entIndex: EntityIndex) {
    // var entName = Entities.GetUnitName(entIndex);
    // var table = CustomNetTables.GetTableValue('construction_size', entName);
    // if (table && table.requires !== undefined) {
    //     var types = table.requires.split(' ');
    //     var value = 0;
    //     for (var i = 0; i < types.length; i++) {
    //         value += GRID_TYPES[types[i]];
    //     }
    //     return value;
    // } else return GRID_TYPES['BUILDABLE'];
    return 0;
}
/**
 * 是否有金矿距离限制
 * @param entIndex
 * @returns
 */
function HasGoldMineDistanceRestriction(entIndex: EntityIndex) {
    // var entName = Entities.GetUnitName(entIndex);
    // var table = CustomNetTables.GetTableValue('construction_size', entName);
    // return table ? table.distance_to_gold_mine : 0;
    return 0;
}
function GetCustomGrid(entIndex: EntityIndex): any[] {
    // var entName = Entities.GetUnitName(entIndex);
    // var table = CustomNetTables.GetTableValue('construction_size', entName);
    // if (table && table.grid !== undefined) return table.grid;
    return [];
}

function GetConstructionSize(entIndex: EntityIndex) {
    // var entName = Entities.GetUnitName(entIndex);
    // var table = CustomNetTables.GetTableValue('construction_size', entName);
    // return table ? table.size : 0;
    return 0;
}
function BlockTreeGrid(position: [number, number, number]) {
    var y = WorldToGridPosX(position[0]) - Root.boundX;
    var x = WorldToGridPosY(position[1]) - Root.boundY;

    if (treeGrid[x] === undefined) treeGrid[x] = [];

    treeGrid[x][y] = GRID_TYPES['BLOCKED'];
}

function BlockGridSquares(position: [number, number, number], squares: number, gridType: string) {
    var halfSide = (squares / 2) * 64;
    var boundingRect = {} as any;
    boundingRect['leftBorderX'] = position[0] - halfSide;
    boundingRect['rightBorderX'] = position[0] + halfSide;
    boundingRect['topBorderY'] = position[1] + halfSide;
    boundingRect['bottomBorderY'] = position[1] - halfSide;

    if (gridType == 'TREE') {
        for (var x = boundingRect['leftBorderX'] + 32; x <= boundingRect['rightBorderX'] - 32; x += 64) {
            for (var y = boundingRect['topBorderY'] - 32; y >= boundingRect['bottomBorderY'] + 32; y -= 64) {
                BlockTreeGrid([x, y, 0]);
            }
        }
    } else {
        for (var x = boundingRect['leftBorderX'] + 32; x <= boundingRect['rightBorderX'] - 32; x += 64) {
            for (var y = boundingRect['topBorderY'] - 32; y >= boundingRect['bottomBorderY'] + 32; y -= 64) {
                BlockEntityGrid([x, y, 0], gridType);
            }
        }
    }
}
function BlockGridInRadius(position: [number, number, number], radius: number, gridType: string) {
    var boundingRect: any = {};
    boundingRect['leftBorderX'] = position[0] - radius;
    boundingRect['rightBorderX'] = position[0] + radius;
    boundingRect['topBorderY'] = position[1] + radius;
    boundingRect['bottomBorderY'] = position[1] - radius;

    for (var x = boundingRect['leftBorderX'] + 32; x <= boundingRect['rightBorderX'] - 32; x += 64) {
        for (var y = boundingRect['topBorderY'] - 32; y >= boundingRect['bottomBorderY'] + 32; y -= 64) {
            if (Length2D(position, [x, y, 0]) <= radius) {
                BlockEntityGrid([x, y, 0], gridType);
            }
        }
    }
}
function SnapToGrid(vec: [number, number, number], size: number) {
    // Buildings are centered differently when the size is odd.
    if (size % 2 != 0) {
        vec[0] = SnapToGrid32(vec[0]);
        vec[1] = SnapToGrid32(vec[1]);
    } else {
        vec[0] = SnapToGrid64(vec[0]);
        vec[1] = SnapToGrid64(vec[1]);
    }
}

function SnapToGrid64(coord: number) {
    return 64 * Math.floor(0.5 + coord / 64);
}

function SnapToGrid32(coord: number) {
    return 32 + 64 * Math.floor(coord / 64);
}

function SnapHeight(x: number, y: number, z: number): [number, number, number] {
    return [x, y, z - ((z + 1) % 128)];
}
function IsBlocked(position: [number, number, number]) {
    var y = WorldToGridPosX(position[0]) - Root.boundX;
    var x = WorldToGridPosY(position[1]) - Root.boundY;

    //$.Msg("IsBlocked: ",position[0], " ", position[1], " ", x, " ", y)

    //{"BLIGHT":8,"BUILDABLE":2,"GOLDMINE":4,"BLOCKED":1}
    // Check height restriction
    if (height_restriction !== undefined && position[2] < height_restriction) return true;

    // Merge grids together into the same value
    var flag = Root.GridNav[x] && Root.GridNav[x][y];
    var entGridValue = entityGrid[x] !== undefined && entityGrid[x][y] !== undefined ? entityGrid[x][y] : GRID_TYPES['BUILDABLE'];
    if (entityGrid[x] && entityGrid[x][y]) flag = flag | entityGrid[x][y];

    // Don't count buildable if its blocked
    var adjust = GRID_TYPES['BUILDABLE'] + GRID_TYPES['BLOCKED'];
    //@ts-ignore
    if ((flag & adjust) == adjust) flag -= GRID_TYPES['BUILDABLE'];
    // If the bits don't match, its invalid
    if ((flag & requires) != requires) return true;

    // If there's a tree standing, its invalid
    //@ts-ignore
    if (update_trees && treeGrid[x] && treeGrid[x][y] & GRID_TYPES['BLOCKED']) return true;

    return false;
}
const StartBuildingHelper = (params?: any) => {
    if (params !== undefined) {
        // Set the parameters passed by AddBuilding
        localHeroIndex = Players.GetPlayerHeroEntityIndex(Players.GetLocalPlayer());
        state = params.state;
        size = params.size;
        range = params.range;
        overlay_size = size + alt_grid_squares * 2;
        builderIndex = params.builderIndex;
        var scale = params.scale;
        var entindex = params.entindex;
        var propScale = params.propScale;
        offsetZ = params.offsetZ;
        modelOffset = params.modelOffset;

        requires = GetRequiredGridType(entindex);
        distance_to_gold_mine = HasGoldMineDistanceRestriction(entindex);

        // If we chose to not recolor the ghost model, set it white
        var ghost_color = [0, 255, 0] as [number, number, number];
        if (!recolor_ghost) ghost_color = [255, 255, 255];

        pressedShift = GameUI.IsShiftDown();

        if (modelParticle !== undefined) {
            Particles.DestroyParticleEffect(modelParticle, true);
        }
        if (propParticle !== undefined) {
            Particles.DestroyParticleEffect(propParticle, true);
        }
        if (gridParticles !== undefined) {
            for (var i in gridParticles) {
                Particles.DestroyParticleEffect(gridParticles[i], true);
            }
        }
        if (overlayParticles !== undefined) {
            for (var i in overlayParticles) {
                Particles.DestroyParticleEffect(overlayParticles[i], true);
            }
        }
        if (rangeOverlay !== undefined) {
            Particles.DestroyParticleEffect(rangeOverlay, true);
        }

        // Building Ghost
        modelParticle = Particles.CreateParticle('particles/buildinghelper/ghost_model.vpcf', ParticleAttachment_t.PATTACH_ABSORIGIN, localHeroIndex);
        Particles.SetParticleControlEnt(
            modelParticle,
            1,
            entindex,
            ParticleAttachment_t.PATTACH_POINT_FOLLOW,
            'follow_hitloc',
            Entities.GetAbsOrigin(entindex),
            true
        );
        Particles.SetParticleControl(modelParticle, 2, ghost_color);
        Particles.SetParticleControl(modelParticle, 3, [model_alpha, 0, 0]);
        Particles.SetParticleControl(modelParticle, 4, [scale, 0, 0]);

        // Grid squares
        gridParticles = [];

        for (var x = 0; x < size * size; x++) {
            var particle = Particles.CreateParticle(
                'particles/buildinghelper/square_sprite.vpcf',
                ParticleAttachment_t.PATTACH_CUSTOMORIGIN,
                0 as EntityIndex
            );
            Particles.SetParticleControl(particle, 1, [32, 0, 0]);
            Particles.SetParticleControl(particle, 3, [grid_alpha, 0, 0]);
            gridParticles.push(particle);
        }

        // Prop particle attachment
        if (params.propIndex !== undefined) {
            propParticle = Particles.CreateParticle(
                'particles/buildinghelper/ghost_model.vpcf',
                ParticleAttachment_t.PATTACH_ABSORIGIN,
                localHeroIndex
            );
            Particles.SetParticleControlEnt(
                propParticle,
                1,
                params.propIndex,
                ParticleAttachment_t.PATTACH_POINT_FOLLOW,
                'follow_hitloc',
                Entities.GetAbsOrigin(params.propIndex),
                true
            );
            Particles.SetParticleControl(propParticle, 2, ghost_color);
            Particles.SetParticleControl(propParticle, 3, [model_alpha, 0, 0]);
            Particles.SetParticleControl(propParticle, 4, [propScale, 0, 0]);
        }

        rangeOverlayActive = false;
        overlayParticles = [];
    }

    if (state == 'active') {
        $.Schedule(frame_rate, StartBuildingHelper);

        // Get all the visible entities
        var entities = Entities.GetAllEntitiesByClassname('npc_dota_building');
        var hero_entities = Entities.GetAllHeroEntities();
        var creature_entities = Entities.GetAllEntitiesByClassname('npc_dota_creature');
        var dummy_entities = Entities.GetAllEntitiesByName('npc_dota_thinker');
        var building_entities = Entities.GetAllBuildingEntities();
        entities = entities.concat(hero_entities);
        entities = entities.concat(building_entities);
        entities = entities.concat(creature_entities);
        entities = entities.concat(dummy_entities);

        // Build the entity grid with the construction sizes and entity origins
        entityGrid = [];
        for (const i of entities.keys()) {
            if (!Entities.IsAlive(entities[i]) || Entities.IsOutOfGame(entities[i])) continue;
            var entPos = Entities.GetAbsOrigin(entities[i]);
            var squares = GetConstructionSize(entities[i]);

            if (squares > 0) {
                // Block squares centered on the origin
                BlockGridSquares(entPos, squares, GRID_TYPES['BLOCKED']);
            } else {
                // Put tree dummies on a separate table to skip trees
                if (Entities.GetUnitName(entities[i]) == 'npc_dota_thinker') {
                    if (Entities.GetAbilityByName(entities[i], 'dummy_tree') != -1) cutTrees[entPos.toString()] = entities[i];
                }
                // Block 2x2 squares if its an enemy unit
                else if (Entities.GetTeamNumber(entities[i]) != Entities.GetTeamNumber(builderIndex)) {
                    BlockGridSquares(entPos, 2, GRID_TYPES['BLOCKED']);
                }
            }

            var specialGrid = GetCustomGrid(entities[i]);
            if (specialGrid) {
                for (var gridType in specialGrid) {
                    if (specialGrid[gridType].Square) {
                        BlockGridSquares(entPos, Number(specialGrid[gridType].Square), (GRID_TYPES as any)[gridType.toUpperCase()]);
                    } else if (specialGrid[gridType].Radius) {
                        BlockGridInRadius(entPos, Number(specialGrid[gridType].Radius), (GRID_TYPES as any)[gridType.toUpperCase()]);
                    }
                }
            }
        }

        // Update treeGrid (slowly, as its the most expensive)
        if (update_trees) {
            var time = Game.GetGameTime();
            var time_since_last_tree_update = time - last_tree_update;
            if (time_since_last_tree_update > tree_update_interval) {
                last_tree_update = time;
                tree_entities = Entities.GetAllEntitiesByClassname('ent_dota_tree');
                for (const i of tree_entities.keys()) {
                    var treePos = Entities.GetAbsOrigin(tree_entities[i]);
                    // Block the grid if the tree isn't chopped
                    if (cutTrees[treePos.toString()] === undefined) BlockGridSquares(treePos, 2, 'TREE');
                }
            }
        }

        var mPos = GameUI.GetCursorPosition();
        var GamePos = Game.ScreenXYToWorld(mPos[0], mPos[1]);
        if (GamePos !== null) {
            SnapToGrid(GamePos, size);

            var invalid;
            var color = [0, 255, 0] as [number, number, number];
            var part = 0;
            var halfSide = (size / 2) * 64;
            var boundingRect: any = {
                leftBorderX: GamePos[0] - halfSide,
                rightBorderX: GamePos[0] + halfSide,
                topBorderY: GamePos[1] + halfSide,
                bottomBorderY: GamePos[1] - halfSide,
            };

            if (GamePos[0] > 10000000) return;

            var closeToGoldMine = TooCloseToGoldmine(GamePos);

            // Building Base Grid
            for (let x = boundingRect.leftBorderX + 32; x <= boundingRect.rightBorderX - 32; x += 64) {
                for (var y = boundingRect.topBorderY - 32; y >= boundingRect.bottomBorderY + 32; y -= 64) {
                    var pos = SnapHeight(x, y, GamePos[2]);
                    if (part > size * size) return;

                    var gridParticle = gridParticles[part];

                    Particles.SetParticleControl(gridParticle, 0, pos);
                    part++;

                    // Grid color turns red when over invalid position
                    color = [0, 255, 0];
                    if (IsBlocked(pos) || closeToGoldMine) {
                        color = [255, 0, 0];
                        invalid = true;
                    }

                    Particles.SetParticleControl(gridParticle, 2, color);
                }
            }

            // Overlay Grid, visible with Alt pressed
            altDown = false; //permanent_alt_grid || GameUI.IsAltDown();
            if (altDown) {
                // Create the particles
                if (overlayParticles && overlayParticles.length == 0) {
                    for (var y = 0; y < overlay_size * overlay_size; y++) {
                        var particle = Particles.CreateParticle(
                            'particles/buildinghelper/square_overlay.vpcf',
                            ParticleAttachment_t.PATTACH_CUSTOMORIGIN,
                            0 as EntityIndex
                        );
                        Particles.SetParticleControl(particle, 1, [32, 0, 0]);
                        Particles.SetParticleControl(particle, 3, [alt_grid_alpha, 0, 0]);
                        overlayParticles.push(particle);
                    }
                }

                color = [255, 255, 255];
                var part2 = 0;
                var halfSide2 = (overlay_size / 2) * 64;
                var boundingRect2: any = {
                    leftBorderX: GamePos[0] - halfSide2,
                    rightBorderX: GamePos[0] + halfSide2,
                    topBorderY: GamePos[1] + halfSide2,
                    bottomBorderY: GamePos[1] - halfSide2,
                };

                for (var x2 = boundingRect2['leftBorderX'] + 32; x2 <= boundingRect2['rightBorderX'] - 32; x2 += 64) {
                    for (var y2 = boundingRect2['topBorderY'] - 32; y2 >= boundingRect2['bottomBorderY'] + 32; y2 -= 64) {
                        var pos2 = SnapHeight(x2, y2, GamePos[2]);
                        if (part2 >= overlay_size * overlay_size) return;

                        color = [255, 255, 255]; //White on empty positions
                        var overlayParticle = overlayParticles[part2];
                        Particles.SetParticleControl(overlayParticle, 0, pos2);
                        part2++;

                        if (IsBlocked(pos2) || TooCloseToGoldmine(pos2)) color = [255, 0, 0];

                        Particles.SetParticleControl(overlayParticle, 2, color);
                    }
                }
            } else {
                // Destroy the particles, only once
                if (overlayParticles && overlayParticles.length != 0) {
                    for (var i in overlayParticles) {
                        Particles.DestroyParticleEffect(overlayParticles[i], true);
                    }
                    overlayParticles = [];
                }
            }

            var modelPos = SnapHeight(GamePos[0], GamePos[1], GamePos[2]);

            // Destroy the range overlay if its not a valid building location
            if (invalid) {
                if (rangeOverlayActive && rangeOverlay !== undefined) {
                    Particles.DestroyParticleEffect(rangeOverlay, true);
                    rangeOverlayActive = false;
                }
            } else {
                if (!rangeOverlayActive) {
                    // redo 圈
                    // rangeOverlay = Particles.CreateParticle("particles/buildinghelper/range_overlay.vpcf", ParticleAttachment_t.PATTACH_CUSTOMORIGIN, localHeroIndex)
                    // Particles.SetParticleControl(rangeOverlay, 1, [range,0,0])
                    // Particles.SetParticleControl(rangeOverlay, 2, [255,255,255])
                    // Particles.SetParticleControl(rangeOverlay, 3, [range_overlay_alpha,0,0])
                    // rangeOverlayActive = true
                }
            }

            if (rangeOverlay !== undefined) Particles.SetParticleControl(rangeOverlay, 0, modelPos);

            // Update the model particle
            modelPos[2] += modelOffset;
            Particles.SetParticleControl(modelParticle, 0, modelPos);

            if (propParticle !== undefined) {
                var pedestalPos = SnapHeight(GamePos[0], GamePos[1], GamePos[2]);
                pedestalPos[2] += offsetZ;
                Particles.SetParticleControl(propParticle, 0, pedestalPos);
            }

            // Turn the model red if we can't build there
            if (turn_red) {
                invalid
                    ? Particles.SetParticleControl(modelParticle, 2, [255, 0, 0])
                    : Particles.SetParticleControl(modelParticle, 2, [255, 255, 255]);
                if (propParticle !== undefined)
                    invalid
                        ? Particles.SetParticleControl(propParticle, 2, [255, 0, 0])
                        : Particles.SetParticleControl(propParticle, 2, [255, 255, 255]);
            }
        }

        if ((!GameUI.IsShiftDown() && pressedShift) || !Entities.IsAlive(builderIndex)) {
            EndBuildingHelper();
        }
    }
};

function EndBuildingHelper() {
    state = 'disabled';
    if (modelParticle !== undefined) {
        Particles.DestroyParticleEffect(modelParticle, true);
    }
    if (propParticle !== undefined) {
        Particles.DestroyParticleEffect(propParticle, true);
    }
    if (rangeOverlay !== undefined) {
        Particles.DestroyParticleEffect(rangeOverlay, true);
    }
    for (var i in gridParticles) {
        Particles.DestroyParticleEffect(gridParticles[i], true);
    }
    for (var i in overlayParticles) {
        Particles.DestroyParticleEffect(overlayParticles[i], true);
    }
}

function IsBuilder(entIndex: number) {
    var tableValue = CustomNetTables.GetTableValue('builders', entIndex.toString());
    return tableValue !== undefined && (tableValue as any).IsBuilder == 1;
}

//#region 初始化内容

console.log(`Hello, 【睡着】 building_helper!`);
_init_Sz_Building_helper();
// Main mouse event callback
GameUI.SetMouseCallback(function (eventName, arg) {
    var CONSUME_EVENT = true;
    var CONTINUE_PROCESSING_EVENT = false;
    var LEFT_CLICK = arg === 0;
    var RIGHT_CLICK = arg === 1;

    if (GameUI.GetClickBehaviors() !== CLICK_BEHAVIORS.DOTA_CLICK_BEHAVIOR_NONE) return CONTINUE_PROCESSING_EVENT;

    var mainSelected = Players.GetLocalPlayerPortraitUnit();

    if (eventName === 'pressed' || eventName === 'doublepressed') {
        // Builder Clicks
        if (IsBuilder(mainSelected))
            if (LEFT_CLICK) return state == 'active' ? SendBuildCommand() : OnLeftButtonPressed();
            else if (RIGHT_CLICK) return OnRightButtonPressed();

        if (LEFT_CLICK) return OnLeftButtonPressed();
        else if (RIGHT_CLICK) return OnRightButtonPressed();
    }
    return CONTINUE_PROCESSING_EVENT;
});

function OnRightButtonPressed() {
    var iPlayerID = Players.GetLocalPlayer();
    var selectedEntities = Players.GetSelectedEntities(iPlayerID);
    var mainSelected = Players.GetLocalPlayerPortraitUnit();
    var targetIndex = GetMouseTarget();
    var pressedShift = GameUI.IsShiftDown();

    // // Builder Right Click
    if (IsBuilder(mainSelected)) {
        //     // Cancel BH
        if (!pressedShift) {
            SendCancelCommand();
        }

        //     // Repair rightclick
        //     if (
        //         right_click_repair &&
        //         IsCustomBuilding(targetIndex) &&
        //         Entities.GetHealthPercent(targetIndex) < 100 &&
        //         IsAlliedUnit(targetIndex, mainSelected)
        //     ) {
        //         GameEvents.SendCustomGameEventToServer('building_helper_repair_command', { targetIndex: targetIndex, queue: pressedShift });
        //         return true;
        //     }
    }

    return false;
}
// Handle Left Button events
function OnLeftButtonPressed() {
    return false;
}
function SendCancelCommand() {
    EndBuildingHelper();
    GameEvents.SendCustomGameEventToServer('building_helper_cancel_command', {} as never);
}

function GetMouseTarget() {
    var mouseEntities = GameUI.FindScreenEntities(GameUI.GetCursorPosition());
    var localHeroIndex = Players.GetPlayerHeroEntityIndex(Players.GetLocalPlayer());

    for (var e of mouseEntities) {
        if (!e.accurateCollision) continue;
        return e.entityIndex;
    }

    for (var e of mouseEntities) {
        return e.entityIndex;
    }

    return 0;
}
function SendBuildCommand() {
    pressedShift = GameUI.IsShiftDown();
    var mainSelected = Players.GetLocalPlayerPortraitUnit();

    var mPos = GameUI.GetCursorPosition();
    var GamePos = Game.ScreenXYToWorld(mPos[0], mPos[1]);
    GameEvents.SendCustomGameEventToServer('building_helper_build_command', {
        builder: mainSelected,
        X: GamePos[0],
        Y: GamePos[1],
        Z: GamePos[2],
        Queue: pressedShift,
    } as never);

    // Cancel unless the player is holding shift
    // if (!GameUI.IsShiftDown())
    // {
    //     EndBuildingHelper(params);
    //     return true;
    // }
    return true;
}
